package io.adrainty.bolee.insurance.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import io.adrainty.bolee.framework.commons.basic.ResponseCode;
import io.adrainty.bolee.framework.commons.exception.AppException;
import io.adrainty.bolee.framework.commons.user.SubjectContentUtil;
import io.adrainty.bolee.framework.commons.user.UserEntity;
import io.adrainty.bolee.framework.commons.utils.EnumUtil;
import io.adrainty.bolee.framework.mq.basic.MQMessage;
import io.adrainty.bolee.framework.mq.basic.MQMessageType;
import io.adrainty.bolee.framework.mq.utils.RabbitMQUtil;
import io.adrainty.bolee.insurance.constants.InsuranceConstant;
import io.adrainty.bolee.insurance.handler.InsuranceHandler;
import io.adrainty.bolee.insurance.handler.InsuranceHandlerRegistry;
import io.adrainty.bolee.insurance.models.entity.*;
import io.adrainty.bolee.insurance.models.request.DoInsuranceDTO;
import io.adrainty.bolee.insurance.models.valobj.OrderStateEnum;
import io.adrainty.bolee.insurance.models.valobj.UnderwritingStateEnum;
import io.adrainty.bolee.insurance.models.valobj.WarrantyStateEnum;
import io.adrainty.bolee.insurance.repository.IWarrantyRepository;
import io.adrainty.bolee.insurance.service.IWarrantyService;
import io.adrainty.bolee.trade.models.ali.AliPeriodicDTO;
import io.adrainty.bolee.trade.models.entity.TradeEntity;
import io.adrainty.bolee.trade.models.valobj.PayChannelEnum;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author AdRainty
 * @version V1.0.0
 * @since 2025/8/26 上午12:55
 */

@Slf4j
@Service
public class WarrantyServiceImpl implements IWarrantyService {

    @Resource
    private IWarrantyRepository warrantyRepository;

    @Resource
    private InsuranceHandlerRegistry insuranceHandlerRegistry;

    @Resource
    private IdentifierGenerator identifierGenerator;

    @Resource
    private StreamBridge streamBridge;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public void saveEarningsOrderBatch(List<WarrantyEarningsOrderEntity> warrantyEarningsOrderList) {
        warrantyRepository.saveEarningsOrderBatch(warrantyEarningsOrderList);
    }

    @Override
    @GlobalTransactional
    public WarrantyEntity doInsure(DoInsuranceDTO doInsuranceDTO) {
        InsuranceHandler handler = insuranceHandlerRegistry.getHandler(doInsuranceDTO.getCheckRule());
        WarrantyEntity warrantyEntity = handler.doInsure(doInsuranceDTO);
        warrantyRepository.saveWarranty(warrantyEntity);
        // 保司承保 跳过
        warrantyEntity.setUnderwritingState(UnderwritingStateEnum.UNDERWRITING_SUCCESS);
        // 同步合同状态
        warrantyRepository.updateWarranty(warrantyEntity);
        // 合同订单生成
        List<WarrantyOrderEntity> orderEntities = handler.createWarrantyOrder(warrantyEntity);
        warrantyRepository.saveOrderBatch(orderEntities);
        // 保存被保人
        List<CustomerRelationEntity> customerRelationVOs = handler.buildCustomerRelationList(
                doInsuranceDTO.getCustomerRelationIds());
        List<WarrantyInsuredEntity> insuredEntities = customerRelationVOs.stream().map(customerRelationVO ->
                WarrantyInsuredEntity.builder()
                        .companyNo(warrantyEntity.getCompanyNo())
                        .warrantyNo(warrantyEntity.getWarrantyNo())
                        .insuredName(customerRelationVO.getName())
                        .insuredIdentityCard(customerRelationVO.getIdentityCard())
                        .build()
        ).toList();
        warrantyRepository.saveInsuredBatch(insuredEntities);
        // 发送队列信息:合同如果超过10分钟不进行支付处理，则会被消息队列清空
        Long messageId = (Long) identifierGenerator.nextId(doInsuranceDTO);
        MQMessage mqMessage = MQMessage.builder()
                .messageId(messageId)
                .content(warrantyEntity.getWarrantyNo())
                .messageType(MQMessageType.WARRANTY_MESSAGE.name())
                .createdTime(LocalDateTime.now())
                .sender("system")
                .build();
        Message<MQMessage> message = MessageBuilder.withPayload(mqMessage)
                .setHeader("x-delay", 10 * 60 * 1000).build();
        streamBridge.send(RabbitMQUtil.getProducerChannelName(MQMessageType.WARRANTY_MESSAGE), message);
        return warrantyEntity;
    }

    @Override
    @Transactional
    public Boolean cleanWarranty(String warrantyNo) {
        // 注意保险合同加锁：防止删除同时，计划任务在进行周期性扣款
        String key = InsuranceConstant.LOCK_WARRANTY_KEY + warrantyNo;
        RLock lock = redissonClient.getLock(key);
        try {
            if (lock.tryLock(5, TimeUnit.SECONDS)) {
                WarrantyEntity queryDTO = WarrantyEntity.builder()
                        .warrantyNo(warrantyNo)
                        .warrantyState(WarrantyStateEnum.WAIT_PAY)
                        .build();
                List<WarrantyEntity> warrantyEntities = warrantyRepository.queryWarranty(queryDTO);
                if (CollUtil.isEmpty(warrantyEntities)) return Boolean.TRUE;
                WarrantyEntity warrantyEntity = warrantyEntities.get(0);
                warrantyRepository.deleteWarranty(warrantyEntity.getId());

                // 删除合同给付计划
                warrantyRepository.deleteEarningsOrderByWarrantyNo(warrantyEntity.getWarrantyNo());
                // 删除合同被保人
                warrantyRepository.deleteInsuredByWarrantyNo(warrantyEntity.getWarrantyNo());
                // 删除合同订单
                warrantyRepository.deleteOrderByWarrantyNo(warrantyEntity.getWarrantyNo());
                // 删除合同核保
                warrantyRepository.deleteVerifyByWarrantyNo(warrantyEntity.getWarrantyNo());
            }
        } catch (Exception e) {
            log.error("Lock error: ", e);
            throw new AppException(ResponseCode.LOCK_ERROR);
        } finally {
            lock.unlock();
        }
        return null;
    }

    @Override
    public String signContract(Long warrantyOrderId, String tradingChannel) {
        WarrantyOrderEntity orderEntity = warrantyRepository.findValidOrderById(warrantyOrderId);
        if (orderEntity == null) {
            throw new AppException(ResponseCode.WARRANTY_ORDER_NOT_EXIST);
        } else if (!OrderStateEnum.WAITING_PAY.equals(orderEntity.getOrderState())) {
            throw new AppException(ResponseCode.TRADE_STATE_ERROR);
        }

        UserEntity userEntity = SubjectContentUtil.getUserVO();
        TradeEntity tradeEntity = TradeEntity.builder()
                .productOrderNo(Long.valueOf(orderEntity.getOrderNo()))
                .tradeChannel(tradingChannel)
                .payerId(userEntity.getId())
                .companyNo(orderEntity.getCompanyNo())
                .payerName(userEntity.getUsername())
                .tradeAmount(BigDecimal.ZERO)
                .refund(BigDecimal.ZERO)
                .isRefund("NO")
                .memo(String.format("保单%s第%s期", orderEntity.getWarrantyNo(), orderEntity.getCurrentPeriod()))
                .build();
        PayChannelEnum channel = EnumUtil.getEnumConstants(tradingChannel, PayChannelEnum.class);
        switch (channel) {
            case ALI_PAY -> {
                AliPeriodicDTO periodicDTO = AliPeriodicDTO.builder()
                        .signScene("INDUSTRY|REPAYMENT")
                        .ruleTotalAmount(String.valueOf(orderEntity.getPremiums()))
                        .ruleSingleAmount(String.valueOf(orderEntity.getPremium()))
                        .ruleTotalPayments(String.valueOf(orderEntity.getPeriods()))
                        .externalAgreementNo(String.valueOf(identifierGenerator.nextId(orderEntity)))
                        .accessChannel("ALIPAYAPP")
                        .contractNo(orderEntity.getWarrantyNo())
                        .build();
                //签约扣款：周期数、周期类型、下次扣款時間
                String rulePeriod = null;
                String rulePeriodType = null;
                if (InsuranceConstant.WEEK.equals(orderEntity.getPeriodicUnit())){
                    rulePeriodType = InsuranceConstant.DAY;
                    rulePeriod ="7";
                }else if (InsuranceConstant.MONTH.equals(orderEntity.getPeriodicUnit())){
                    rulePeriodType = InsuranceConstant.MONTH;
                    rulePeriod ="1";
                }else if (InsuranceConstant.YEAR.equals(orderEntity.getPeriodicUnit())){
                    rulePeriodType = InsuranceConstant.MONTH;
                    rulePeriod ="12";
                }
                periodicDTO.setRulePeriod(rulePeriod);
                periodicDTO.setRulePeriodType(rulePeriodType);
                periodicDTO.setRuleExecuteTime(orderEntity.getScheduleTime()
                        .format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                tradeEntity.setAliPeriodicVO(periodicDTO);
            }
            default -> throw new AppException(ResponseCode.PAY_CHANNEL_NOT_EXIST);
        }
        return "";
    }
}
